﻿using System;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;

namespace QueryResource.Common
{
    public class ToExcel
    {
        public static string ExcelHeader()
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            sb.Append("<?xml version=\"1.0\"?>\n");
            sb.Append("<?mso-application progid=\"Excel.Sheet\"?>\n");
            sb.Append(
              "<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\" ");
            sb.Append("xmlns:o=\"urn:schemas-microsoft-com:office:office\" ");
            sb.Append("xmlns:x=\"urn:schemas-microsoft-com:office:excel\" ");
            sb.Append("xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\" ");
            sb.Append("xmlns:html=\"http://www.w3.org/TR/REC-html40\">\n");
            sb.Append(
              "<DocumentProperties xmlns=\"urn:schemas-microsoft-com:office:office\">");
            sb.Append("</DocumentProperties>");
            sb.Append(
              "<ExcelWorkbook xmlns=\"urn:schemas-microsoft-com:office:excel\">\n");
            sb.Append("<ProtectStructure>False</ProtectStructure>\n");
            sb.Append("<ProtectWindows>False</ProtectWindows>\n");
            sb.Append("</ExcelWorkbook>\n");
            return sb.ToString();
        }
        public static void ExportToExcel(DataSet source, string fileName,string[] filterColumn)
        {
            StreamWriter excelDoc = new StreamWriter(fileName);
            string startExcelXml = ExcelHeader();
            startExcelXml += "<Styles>\r\n " +
                "<Style ss:ID=\"Default\" ss:Name=\"Normal\">\r\n " +
                "<Alignment ss:Vertical=\"Bottom\"/>\r\n <Borders/>" +
                "\r\n <Font/>\r\n <Interior/>\r\n <NumberFormat/>" +
                "\r\n <Protection/>\r\n </Style>\r\n " +
                "<Style ss:ID=\"BoldColumn\">\r\n <Font " +
                "x:Family=\"Swiss\" ss:Bold=\"1\"/>\r\n </Style>\r\n " +
                "<Style ss:ID=\"StringLiteral\">\r\n <NumberFormat" +
                " ss:Format=\"@\"/>\r\n </Style>\r\n <Style " +
                "ss:ID=\"Decimal\">\r\n <NumberFormat " +
                "ss:Format=\"0.0000\"/>\r\n </Style>\r\n " +
                "<Style ss:ID=\"Integer\">\r\n <NumberFormat " +
                "ss:Format=\"0\"/>\r\n </Style>\r\n <Style " +
                "ss:ID=\"DateLiteral\">\r\n <NumberFormat " +
                "ss:Format=\"mm/dd/yyyy;@\"/>\r\n </Style>\r\n " +
                "</Styles>\r\n ";
            const string endExcelXml = "</Workbook>";

            int rowCount = 0;
            int sheetCount = 1;
          
            excelDoc.Write(startExcelXml);
            excelDoc.Write("<Worksheet ss:Name=\"Sheet" + sheetCount + "\">");
            excelDoc.Write("<Table>");
            excelDoc.Write("<Row>");
            for (int x = 0; x < source.Tables[0].Columns.Count; x++)
            {
                string columnName = source.Tables[0].Columns[x].ColumnName;
                if (filterColumn != null && filterColumn.Contains(columnName))
                    continue;
                excelDoc.Write("<Cell ss:StyleID=\"BoldColumn\"><Data ss:Type=\"String\">");
                excelDoc.Write(columnName);
                excelDoc.Write("</Data></Cell>");
            }
            excelDoc.Write("</Row>");
            foreach (DataRow x in source.Tables[0].Rows)
            {
                rowCount++;
//Excel 最多只能显示64000条数据，在同一个WorkSheet里
                if (rowCount == 64000)
                {
                    rowCount = 0;
                    sheetCount++;
                    excelDoc.Write("</Table>");
                    excelDoc.Write(" </Worksheet>");
                    excelDoc.Write("<Worksheet ss:Name=\"Sheet" + sheetCount + "\">");
                    excelDoc.Write("<Table>");
                }
                excelDoc.Write("<Row>"); //ID=" + rowCount + "
                for (int y = 0; y < source.Tables[0].Columns.Count; y++)
                {
                    string columnName = source.Tables[0].Columns[y].ColumnName;
                    if (filterColumn != null && filterColumn.Contains(columnName))
                        continue;
                    Type rowType = x[y].GetType();
                    switch (rowType.ToString())
                    {
                        case "System.String":
                            string xmLstring = x[y].ToString();
                            xmLstring = xmLstring.Trim();
                            //xmLstring = xmLstring.Replace("&", "&");
                            //xmLstring = xmLstring.Replace(">", ">");
                            //xmLstring = xmLstring.Replace("<", "<");
                            xmLstring = System.Security.SecurityElement.Escape(xmLstring);
                            excelDoc.Write("<Cell ss:StyleID=\"StringLiteral\">" +
                                           "<Data ss:Type=\"String\">");
                            excelDoc.Write(xmLstring);
                            excelDoc.Write("</Data></Cell>");
                            break;
                        case "System.DateTime": 

                            DateTime xmlDate = (DateTime)x[y];
                            string xmlDatetoString = ""; //Excel Converted Date
                            if (xmlDate != DateTime.MinValue)
                            {
                                xmlDatetoString = xmlDate.Year.ToString(CultureInfo.InvariantCulture) +
                                     "-" +
                                     (xmlDate.Month < 10 ? "0" +
                                     xmlDate.Month.ToString(CultureInfo.InvariantCulture) : xmlDate.Month.ToString(CultureInfo.InvariantCulture)) +
                                     "-" +
                                     (xmlDate.Day < 10 ? "0" +
                                     xmlDate.Day.ToString(CultureInfo.InvariantCulture) : xmlDate.Day.ToString(CultureInfo.InvariantCulture)) +
                                     "T" +
                                     (xmlDate.Hour < 10 ? "0" +
                                     xmlDate.Hour.ToString(CultureInfo.InvariantCulture) : xmlDate.Hour.ToString(CultureInfo.InvariantCulture)) +
                                     ":" +
                                     (xmlDate.Minute < 10 ? "0" +
                                     xmlDate.Minute.ToString(CultureInfo.InvariantCulture) : xmlDate.Minute.ToString(CultureInfo.InvariantCulture)) +
                                     ":" +
                                     (xmlDate.Second < 10 ? "0" +
                                     xmlDate.Second.ToString(CultureInfo.InvariantCulture) : xmlDate.Second.ToString(CultureInfo.InvariantCulture)) +
                                     ".000";
                            }
                            excelDoc.Write("<Cell ss:StyleID=\"DateLiteral\">" +
                                         "<Data ss:Type=\"DateTime\">");
                            excelDoc.Write(xmlDatetoString);
                            excelDoc.Write("</Data></Cell>");
                            break;
                        case "System.Boolean":
                            excelDoc.Write("<Cell ss:StyleID=\"StringLiteral\">" +
                                        "<Data ss:Type=\"String\">");
                            excelDoc.Write(x[y].ToString());
                            excelDoc.Write("</Data></Cell>");
                            break;
                        case "System.Int16":
                        case "System.Int32":
                        case "System.Int64":
                        case "System.Byte":
                            excelDoc.Write("<Cell ss:StyleID=\"Integer\">" +
                                    "<Data ss:Type=\"Number\">");
                            excelDoc.Write(x[y].ToString());
                            excelDoc.Write("</Data></Cell>");
                            break;
                        case "System.Decimal":
                        case "System.Double":
                            excelDoc.Write("<Cell ss:StyleID=\"Decimal\">" +
                                  "<Data ss:Type=\"Number\">");
                            excelDoc.Write(x[y].ToString());
                            excelDoc.Write("</Data></Cell>");
                            break;
                        case "System.DBNull":
                            excelDoc.Write("<Cell ss:StyleID=\"StringLiteral\">" +
                                  "<Data ss:Type=\"String\">");
                            excelDoc.Write("");
                            excelDoc.Write("</Data></Cell>");
                            break;
                        default:
                            throw (new Exception(rowType + " not handled."));
                    }
                    //ProgressEventArgs pe = new ProgressEventArgs(position);
                    //OnProgressChange(pe);
                }
                excelDoc.Write("</Row>");
            }
            excelDoc.Write("</Table>");
            excelDoc.Write(" </Worksheet>");
            excelDoc.Write(endExcelXml);
            excelDoc.Close();
        }
    }
}
